package com.zm.xiaohashu.relation.biz.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.RandomUtil;
import cn.hutool.json.JSONUtil;
import com.alibaba.nacos.shaded.com.google.common.base.Preconditions;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.zm.exception.BizException;
import com.zm.response.PageResponse;
import com.zm.response.Response;
import com.zm.utils.DateUtils;
import com.zm.xiaohashu.biz.context.utils.LoginUserContextHolder;
import com.zm.xiaohashu.relation.biz.constant.MQConstants;
import com.zm.xiaohashu.relation.biz.constant.RedisKeyConstants;
import com.zm.xiaohashu.relation.biz.domain.Fans;
import com.zm.xiaohashu.relation.biz.domain.Following;
import com.zm.xiaohashu.relation.biz.enums.FollowLuaResultEnum;
import com.zm.xiaohashu.relation.biz.enums.ResponseCodeEnum;
import com.zm.xiaohashu.relation.biz.enums.UnfollowLuaResultEnum;
import com.zm.xiaohashu.relation.biz.mapper.FansMapper;
import com.zm.xiaohashu.relation.biz.mapper.FollowingMapper;
import com.zm.xiaohashu.relation.biz.model.dto.FollowUserMqDTO;
import com.zm.xiaohashu.relation.biz.model.dto.UnfollowUserMqDTO;
import com.zm.xiaohashu.relation.biz.model.vo.*;
import com.zm.xiaohashu.relation.biz.rpc.UserRpcService;
import com.zm.xiaohashu.relation.biz.service.RelationService;
import com.zm.xiaohashu.user.model.vo.FindUserByIdRspDTO;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.apache.rocketmq.client.producer.SendCallback;
import org.apache.rocketmq.client.producer.SendResult;
import org.apache.rocketmq.spring.core.RocketMQTemplate;
import org.springframework.core.io.ClassPathResource;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.messaging.Message;
import org.springframework.messaging.support.MessageBuilder;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.scripting.support.ResourceScriptSource;
import org.springframework.stereotype.Service;

import java.time.LocalDateTime;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
import java.util.Set;


@Service
@Slf4j

public class RelationServiceImpl implements RelationService {
    @Resource
    private FollowingMapper followingMapper;
    @Resource
    private FansMapper fansMapper;
    @Resource
    private UserRpcService userRpcService;
    @Resource
    private RocketMQTemplate rocketMQTemplate;
    @Resource
    private RedisTemplate<String, Object> redisTemplate;

    @Resource(name = "taskExecutor")
    private ThreadPoolTaskExecutor threadPoolTaskExecutor;

    /**
     * 关注的逻辑
     * 1. 首先就是先判断关注的用户是否存在
     * 2. 判断是否已经关注了该用户 这里就是使用lua脚本checkAndAddScript去做三个判断 第一，关注列表是都存在，第二，该用户是否已经关注了该用户，第三，看用户的关注是否上限，第四，添加用户
     * 如果关注列表不存在，那么就先去就去查数据的数据，数据库查的是当前用户的关注列表，如果不为空那么就就行一个全量同步,然后再次执行一次刚才那个三个检查的脚本checkAndAddScript，不存在的话就会直接添加，存在抛异常
     * 如果数据库中没有数据，那么就直接去初始化一个zset集合然后添加进去
     * 然后发送mq的顺序消息去进行消费，消费者那里做了一个限流的操作，然后进行落库，成功以后看一下redis中是否存在被关注用户的一个粉丝列表，有的话更新，没有就不做任何操作
     * 然后发送mq去计数，处理粉丝数的话做了一个聚合写的操作
     */
    @Override
    public Response<?> follow(FollowUserReqVO followUserReqVO) {
        Long followUserId = followUserReqVO.getFollowUserId();
        Long userId = LoginUserContextHolder.getUserId();
        //不能关注自己
        Preconditions.checkArgument(!Objects.equals(userId, followUserId), ResponseCodeEnum.CANT_FOLLOW_YOUR_SELF.getErrorMessage());
        FindUserByIdRspDTO findUserByIdRspDTO = userRpcService.findById(followUserId);
        //用户不存在
        Preconditions.checkArgument(Objects.nonNull(findUserByIdRspDTO), ResponseCodeEnum.FOLLOW_USER_NOT_EXISTED.getErrorMessage());
        String followingRedisKey = RedisKeyConstants.buildUserFollowingKey(userId);
        DefaultRedisScript<Long> checkAndAddScript = new DefaultRedisScript<>();
        checkAndAddScript.setResultType(Long.class);
        //检查--没有的话直接添加
        checkAndAddScript.setScriptSource(new ResourceScriptSource(new ClassPathResource("/lua/follow/follow_check_and_add.lua")));
        long timestamp = DateUtils.localDateTime2Timestamp(LocalDateTime.now());
        Long res = redisTemplate.execute(checkAndAddScript, Collections.singletonList(followingRedisKey), followUserId, timestamp);
        FollowLuaResultEnum luaResultEnum = FollowLuaResultEnum.valueOf(res);
        //先做前面的判断
        long expireSeconds = 60 * 60 * 24 + RandomUtil.randomInt(60 * 60 * 24);
        checkRedisFollowingListLimitAndAlready(luaResultEnum);
        if (Objects.equals(FollowLuaResultEnum.ZSET_NOT_EXIST, luaResultEnum)) {
            //查数据库，数据库有记录就异步同步数据到redis，没有数据就初始化zset列表
            LambdaQueryWrapper<Following> followingLqw = new LambdaQueryWrapper<>();
            followingLqw.eq(Following::getUserId, userId);
            List<Following> followingList = followingMapper.selectList(followingLqw);
            if (CollUtil.isNotEmpty(followingList)) {
                //同步到redis
                DefaultRedisScript<Long> followingfBatchAddToRedis = new DefaultRedisScript<>();
                followingfBatchAddToRedis.setResultType(Long.class);
                followingfBatchAddToRedis.setScriptSource(new ResourceScriptSource(new ClassPathResource("/lua/follow/follow_batch_add_and_expire.lua")));
                Object[] luaArgs = buildLuaArgs(followingList, expireSeconds);
                redisTemplate.execute(followingfBatchAddToRedis, Collections.singletonList(followingRedisKey), luaArgs);
                log.info("同步用户关注关系到redis成功");
                //再次执行检查的脚本
                Long checkAgian = redisTemplate.execute(checkAndAddScript, Collections.singletonList(followingRedisKey), followUserId, timestamp);
                checkRedisFollowingListLimitAndAlready(FollowLuaResultEnum.valueOf(checkAgian));//再次检查可以的话添加成功，没有的话就抛出异常
            } else {
                //3.数据库不存在，redis也不存在，初始化一个zset集合
                //过期时间保底一天+随机秒数
                DefaultRedisScript<Long> addAndExpireScript = new DefaultRedisScript<>();
                addAndExpireScript.setResultType(Long.class);
                addAndExpireScript.setScriptSource(new ResourceScriptSource(new ClassPathResource("/lua/follow/follow_add_and_expire.lua")));
                Long execute = redisTemplate.execute(addAndExpireScript, Collections.singletonList(followingRedisKey), followUserId, timestamp, expireSeconds);
                log.info("execute:{}=======", execute);
            }
        }
        //异步去落地db--要保证消息的顺序性
        String hashKey = userId.toString();//保证消息顺序的关键
        String destinatino = MQConstants.TOPIC_FOLLOW_OR_UNFOLLOW + ":" + MQConstants.TAG_FOLLOW;//带标签的消息
        FollowUserMqDTO followUserMqDTO = FollowUserMqDTO.builder()
                .userId(userId)
                .followUserId(followUserId)
                .createTime(LocalDateTime.now())
                .build();
        Message<String> message = MessageBuilder.withPayload(JSONUtil.toJsonStr(followUserMqDTO)).build();
        rocketMQTemplate.asyncSendOrderly(destinatino, message, hashKey, new SendCallback() {
            @Override
            public void onSuccess(SendResult sendResult) {
                log.info("==> 【关注服务：关注记录】MQ 推送成功，SendResult: {}", sendResult);
            }

            @Override
            public void onException(Throwable throwable) {
                log.error("==> 【关注服务：关注记录】MQ 推送异常: ", throwable);
            }
        });
        return Response.success();
    }

    /**
     * 取关逻辑
     * 首先先检查一下redis中是否有该用户，有则直接删除，然后发送mq去落地数据库
     * 没有则去数据库中查询该用户是否存在关注列表，有的话先同步有一下redis然后再次执行删除逻辑然后发送mq落地数据库
     */
    @Override
    public Response<?> unfollow(UnfollowUserReqVO unfollowUserReqVO) {
        Long unfollowUserId = unfollowUserReqVO.getUnfollowUserId();
        Long userId = LoginUserContextHolder.getUserId();
        //1.不能取关自己
        Preconditions.checkArgument(!Objects.equals(userId, unfollowUserId), ResponseCodeEnum.CANT_UNFOLLOW_YOUR_SELF.getErrorMessage());
        //判断zset是否存在
        String userFollowKey = RedisKeyConstants.buildUserFollowingKey(userId);
        DefaultRedisScript<Long> unfollowCheckAndDelScript = new DefaultRedisScript<>();
        unfollowCheckAndDelScript.setResultType(Long.class);
        unfollowCheckAndDelScript.setScriptSource(new ResourceScriptSource(new ClassPathResource("/lua/unfollow/unfollow_check_and_del.lua")));
        Long unfollowRes = redisTemplate.execute(unfollowCheckAndDelScript, Collections.singletonList(userFollowKey), unfollowUserId);
        UnfollowLuaResultEnum unfollowLuaResultEnum = UnfollowLuaResultEnum.valueOf(unfollowRes);
        switch (unfollowLuaResultEnum) {
            //未关注对方
            case UN_FOLLOWED -> throw new BizException(ResponseCodeEnum.NOT_FOLLOWED);
            case ZSET_NOT_EXIST -> {
                LambdaQueryWrapper<Following> followLqw = new LambdaQueryWrapper<>();
                followLqw.eq(Following::getUserId, userId);
                List<Following> followingList = followingMapper.selectList(followLqw);
                if (CollectionUtil.isNotEmpty(followingList)) {
                    //同步一下关注列表然后再次执行第一次的检查-移除操作
                    //过期时间保底一天——随机秒数
                    long expireSeconds = 60 * 60 * 24 + RandomUtil.randomInt(60 * 60 * 24);
                    Object[] luaArgs = buildLuaArgs(followingList, expireSeconds);
                    DefaultRedisScript<Long> batchAddAndExpireScript = new DefaultRedisScript<>();
                    batchAddAndExpireScript.setResultType(Long.class);
                    batchAddAndExpireScript.setScriptSource(new ResourceScriptSource(new ClassPathResource("/lua/unfollow/unfollow_batch_add_and_expire.lua")));
                    redisTemplate.execute(batchAddAndExpireScript, Collections.singletonList(userFollowKey), luaArgs);
                    //再次执行脚本
                    unfollowRes = redisTemplate.execute(unfollowCheckAndDelScript, Collections.singletonList(userFollowKey), unfollowUserId);
                    if (Objects.equals(unfollowRes, UnfollowLuaResultEnum.UN_FOLLOWED.getCode())) {
                        throw new BizException(ResponseCodeEnum.NOT_FOLLOWED);
                    }
                } else {
                    throw new BizException(ResponseCodeEnum.NOT_FOLLOWED);
                }
            }
        }
        //发送mq去落库
        UnfollowUserMqDTO unfollowUserMqDT = UnfollowUserMqDTO.builder()
                .userId(userId)
                .unfollowUserId(unfollowUserId)
                .createTime(LocalDateTime.now())
                .build();
        String destination = MQConstants.TOPIC_FOLLOW_OR_UNFOLLOW + ":" + MQConstants.TAG_UNFOLLOW;
        Message<String> message = MessageBuilder.withPayload(JSONUtil.toJsonStr(unfollowUserMqDT)).build();
        String hashKey = String.valueOf(userId);
        rocketMQTemplate.asyncSendOrderly(destination, message, hashKey, new SendCallback() {
            @Override
            public void onSuccess(SendResult sendResult) {
                log.info("==> 取关消息发送成功，SendResult: {}", sendResult);
            }

            @Override
            public void onException(Throwable throwable) {
                log.error("==> 取关消息发送异常: ", throwable);
            }
        });
        return Response.success();
    }

    /**
     * 获取用户关注列表
     */
    @Override
    public PageResponse<FindFollowingUserRspVO> findFollowingList(FindFollowingListReqVO findFollowingListReqVO) {
        Integer pageNo = findFollowingListReqVO.getPageNo();
        Long userId = findFollowingListReqVO.getUserId();
        String followingListRedisKey = RedisKeyConstants.buildUserFollowingKey(userId);
        // 查询目标用户关注列表 ZSet 的总大小
        long total = redisTemplate.opsForZSet().zCard(followingListRedisKey);
        // 查询目标用户关注列表 ZSet 的总大小
        // 返参
        List<FindFollowingUserRspVO> findFollowingUserRspVOS = null;
        if (total > 0) {
            // 每页展示 10 条数据
            long limit = 10;
            // 计算一共多少页
//            long totalPage = PageResponse.getTotalPage(total, limit);
            long totalPage = total / limit;
            // 请求的页码超出了总页数
            if (pageNo > totalPage) return PageResponse.success(null, pageNo, total);
            // 准备从 Redis 中查询 ZSet 分页数据
            // 每页 10 个元素，计算偏移量
            long offset = (pageNo - 1) * limit;
            Set<Object> followingUserIdsSet = redisTemplate.opsForZSet()
                    .reverseRangeByScore(followingListRedisKey, Double.NEGATIVE_INFINITY, Double.POSITIVE_INFINITY, offset, limit);
            if (CollUtil.isNotEmpty(followingUserIdsSet)) {
                // 提取所有用户 ID 到集合中
                List<Long> userIds = followingUserIdsSet.stream().map(object -> Long.valueOf(object.toString())).toList();

                // RPC: 批量查询用户信息
                List<FindUserByIdRspDTO> findUserByIdRspDTOS = userRpcService.findByIds(userIds);

                // 若不为空，DTO 转 VO
                if (CollUtil.isNotEmpty(findUserByIdRspDTOS)) {
                    findFollowingUserRspVOS = findUserByIdRspDTOS.stream()
                            .map(dto -> FindFollowingUserRspVO.builder()
                                    .userId(dto.getId())
                                    .avatar(dto.getAvatar())
                                    .nickname(dto.getNickName())
                                    .introduction(dto.getIntroduction())
                                    .build())
                            .toList();
                }
            }
        } else {
            // 若 Redis 中没有数据，则从数据库查询
            LambdaQueryWrapper<Following> followLqw = new LambdaQueryWrapper<>();
            followLqw.eq(Following::getUserId, userId);
            Long count = followingMapper.selectCount(followLqw);
            Page<Following> page = new Page<>(pageNo, 10);
            followingMapper.selectPage(page, followLqw);
            // 计算一共多少页
            long totalPage = page.getTotal();
            // 请求的页码超出了总页数
            if (pageNo > totalPage) return PageResponse.success(null, pageNo, count);

            List<Following> records = page.getRecords();
            if (CollUtil.isNotEmpty(records)) {
                List<Long> followingUserIds = records.stream().map(Following::getFollowingUserId).toList();
                List<FindUserByIdRspDTO> findUserByIdRspDTOS = userRpcService.findByIds(followingUserIds);
                findFollowingUserRspVOS = findUserByIdRspDTOS.stream().map(findUserByIdRspDTO -> {
                    FindFollowingUserRspVO findFollowingUserRspVO = FindFollowingUserRspVO.builder()
                            .userId(findUserByIdRspDTO.getId())
                            .avatar(findUserByIdRspDTO.getAvatar())
                            .nickname(findUserByIdRspDTO.getNickName())
                            .introduction(findUserByIdRspDTO.getIntroduction())
                            .build();
                    return findFollowingUserRspVO;
                }).toList();
            }
            //异步将关注列表全量同步到 Redis
            threadPoolTaskExecutor.execute(() -> {
                syncFollowingList2Redis(userId);
            });
        }
        return PageResponse.success(findFollowingUserRspVOS, pageNo, total);
    }

    @Override
    public PageResponse<FindFansUserRspVO> findFansList(FindFansListReqVO findFansListReqVO) {
        Integer pageNo = findFansListReqVO.getPageNo();
        Long userId = findFansListReqVO.getUserId();
        String fansListRedisKey = RedisKeyConstants.buildUserFansKey(userId);
        long total = redisTemplate.opsForZSet().zCard(fansListRedisKey);
        long limit = 10;
        // 返参
        List<FindFansUserRspVO> findFansUserRspVOS = null;
        if (total > 0) {
            long totalPage = PageResponse.getTotalPage(total, limit);
            long offset = (pageNo - 1) * limit;
            // 请求的页码超出了总页数
            if (pageNo > totalPage) return PageResponse.success(null, pageNo, total);
            Set<Object> fansUserIdsSet = redisTemplate.opsForZSet().reverseRangeByScore(fansListRedisKey, Double.NEGATIVE_INFINITY, Double.POSITIVE_INFINITY, offset, limit);
            if (CollUtil.isNotEmpty(fansUserIdsSet)) {
                // 提取所有用户 ID 到集合中
                List<Long> userIds = fansUserIdsSet.stream().map(object -> Long.valueOf(object.toString())).toList();
                // RPC: 批量查询用户信息
                findFansUserRspVOS = rpcUserServiceAndCountServiceAndDTO2VO(userIds, findFansUserRspVOS);
            }
        } else {
            LambdaQueryWrapper<Fans> lqw = new LambdaQueryWrapper<>();
            lqw.eq(Fans::getUserId, userId);
            Page<Fans> page = new Page<>(pageNo, limit);
            fansMapper.selectPage(page, lqw);
            // 计算一共多少页
            long totalPage = page.getTotal();
            // 请求的页码超出了总页数（只允许查询前 500 页）
            if (pageNo > 500 || pageNo > totalPage) return PageResponse.success(null, pageNo, total);
            List<Fans> fansList = page.getRecords();
            if (CollUtil.isNotEmpty(fansList)) {
                // 提取所有粉丝用户 ID 到集合中
                List<Long> userIds = fansList.stream().map(Fans::getFansUserId).toList();
                // RPC: 调用用户服务、计数服务，并将 DTO 转换为 VO
                findFansUserRspVOS = rpcUserServiceAndCountServiceAndDTO2VO(userIds, findFansUserRspVOS);
                // 异步将粉丝列表同步到 Redis（最多5000条）
                threadPoolTaskExecutor.submit(() -> syncFansList2Redis(userId));
            }
        }
        return PageResponse.success(findFansUserRspVOS, pageNo, total);
    }

    /**
     * 异步同步粉丝列到redis
     * @param userId
     */
    private void syncFansList2Redis(Long userId) {
        LambdaQueryWrapper<Fans> fansLqw = new LambdaQueryWrapper<>();
        Page<Fans> page = new Page<>(1, 5000);
        fansLqw.eq(Fans::getUserId, userId);
        // 查询粉丝列表（最多5000位用户）
        List<Fans> fansDOS = fansMapper.selectList(page, fansLqw);
        if (CollUtil.isNotEmpty(fansDOS)) {
            // 用户粉丝列表 Redis Key
            String fansListRedisKey = RedisKeyConstants.buildUserFansKey(userId);
            // 随机过期时间
            // 保底1天+随机秒数
            long expireSeconds = 60 * 60 * 24 + RandomUtil.randomInt(60 * 60 * 24);
            // 构建 Lua 参数
            Object[] luaArgs = buildFansZSetLuaArgs(fansDOS, expireSeconds);
            // 执行 Lua 脚本，批量同步关注关系数据到 Redis 中
            DefaultRedisScript<Long> script = new DefaultRedisScript<>();
            script.setScriptSource(new ResourceScriptSource(new ClassPathResource("/lua/follow/follow_batch_add_and_expire.lua")));
            script.setResultType(Long.class);
            redisTemplate.execute(script, Collections.singletonList(fansListRedisKey), luaArgs);
        }
    }

    /**
     * 构建 Lua 脚本参数：粉丝列表
     */
    private static Object[] buildFansZSetLuaArgs(List<Fans> fansDOS, long expireSeconds) {
        int argsLength = fansDOS.size() * 2 + 1; // 每个粉丝关系有 2 个参数（score 和 value），再加一个过期时间
        Object[] luaArgs = new Object[argsLength];

        int i = 0;
        for (Fans fansDO : fansDOS) {
            luaArgs[i] = DateUtils.localDateTime2Timestamp(fansDO.getCreateTime()); // 粉丝的关注时间作为 score
            luaArgs[i + 1] = fansDO.getFansUserId();          // 粉丝的用户 ID 作为 ZSet value
            i += 2;
        }

        luaArgs[argsLength - 1] = expireSeconds; // 最后一个参数是 ZSet 的过期时间
        return luaArgs;
    }


    /**
     * 构建粉丝列表的返参
     */
    private List<FindFansUserRspVO> rpcUserServiceAndCountServiceAndDTO2VO(List<Long> userIds, List<FindFansUserRspVO> findFansUserRspVOS) {
        List<FindUserByIdRspDTO> findUserByIdRspDTOS = userRpcService.findByIds(userIds);
        // 若不为空，DTO 转 VO
        if (CollUtil.isNotEmpty(findUserByIdRspDTOS)) {
            findFansUserRspVOS = findUserByIdRspDTOS.stream()
                    .map(dto -> FindFansUserRspVO.builder()
                            .userId(dto.getId())
                            .avatar(dto.getAvatar())
                            .nickname(dto.getNickName())
                            .noteTotal(0L) // TODO: 这块的数据暂无，后续补充
                            .fansTotal(0L) // TODO: 这块的数据暂无，后续补充
                            .build())
                    .toList();
        }
        return findFansUserRspVOS;
    }


    /**
     * 判断是否已经关注了该用户，列表已满
     */
    private void checkRedisFollowingListLimitAndAlready(FollowLuaResultEnum luaResultEnum) {
        switch (luaResultEnum) {
            case FOLLOW_LIMIT -> throw new BizException(ResponseCodeEnum.FOLLOWING_COUNT_LIMIT);
            case ALREADY_FOLLOWED -> throw new BizException(ResponseCodeEnum.ALREADY_FOLLOWED);
        }
    }

    /**
     * 构建 Lua 脚本参数
     */
    private static Object[] buildLuaArgs(List<Following> followingDOS, long expireSeconds) {
        int argsLength = followingDOS.size() * 2 + 1; // 每个关注关系有 2 个参数（score 和 value），再加一个过期时间
        Object[] luaArgs = new Object[argsLength];

        int i = 0;
        for (Following following : followingDOS) {
            luaArgs[i] = DateUtils.localDateTime2Timestamp(following.getCreateTime()); // 关注时间作为 score
            luaArgs[i + 1] = following.getFollowingUserId();          // 关注的用户 ID 作为 ZSet value
            i += 2;
        }

        luaArgs[argsLength - 1] = expireSeconds; // 最后一个参数是 ZSet 的过期时间
        return luaArgs;
    }


    /**
     * 全量同步关注列表至 Redis 中
     */
    private void syncFollowingList2Redis(Long userId) {
        // 查询全量关注用户列表（1000位用户）
        LambdaQueryWrapper<Following> lqw = new LambdaQueryWrapper<>();
        lqw.eq(Following::getUserId, userId);
        List<Following> followingDOS = followingMapper.selectList(lqw);
        if (CollUtil.isNotEmpty(followingDOS)) {
            // 用户关注列表 Redis Key
            String followingListRedisKey = RedisKeyConstants.buildUserFollowingKey(userId);
            // 随机过期时间
            // 保底1天+随机秒数
            long expireSeconds = 60 * 60 * 24 + RandomUtil.randomInt(60 * 60 * 24);
            // 构建 Lua 参数
            Object[] luaArgs = buildLuaArgs(followingDOS, expireSeconds);

            // 执行 Lua 脚本，批量同步关注关系数据到 Redis 中
            DefaultRedisScript<Long> script = new DefaultRedisScript<>();
            script.setScriptSource(new ResourceScriptSource(new ClassPathResource("/lua/follow/follow_batch_add_and_expire.lua")));
            script.setResultType(Long.class);
            redisTemplate.execute(script, Collections.singletonList(followingListRedisKey), luaArgs);
        }
    }
}
